/*
 *   Copyright (C) 2010  Jens Kieselbach
 * 
 *   This program is free software: you can redistribute it and/or modify
 *   it under the terms of the GNU General Public License as published by
 *   the Free Software Foundation, either version 3 of the License, or
 *   (at your option) any later version.
 * 
 *   This program is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU General Public License for more details.
 * 
 *   You may obtain a copy of the License at http://www.gnu.org/licenses/gpl.html
 */

package de.kieselbach.swing

import java.awt.Graphics
import java.awt.Image
import java.awt.Point
import java.awt.event.MouseAdapter
import java.awt.event.MouseEvent
import java.awt.event.MouseMotionAdapter
import java.awt.image.BufferedImage
import java.util.HashMap
import java.util.Map

import javax.swing.Icon
import javax.swing.ImageIcon
import javax.swing.JButton
import javax.swing.JLabel
import javax.swing.JPopupMenu
import javax.swing.event.PopupMenuEvent
import javax.swing.event.PopupMenuListener

class SplitButton extends JButton {

    static final String ICON_NORMAL = "normal"
    static final String ICON_PRESSED = "pressed"
    static final String ICON_ROLLOVER = "rollover"
    static final String ICON_ROLLOVER_SELECTED = "rolloverSelected"
    static final String ICON_SELECTED = "selected"
    static final String ICON_DISABLED = "disabled"
    static final String ICON_DISABLED_SELECTED = "disabledSelected"

    static final String ICON_ROLLOVER_LINE = "rolloverLine"
    static final String ICON_ROLLOVER_SELECTED_LINE = "rolloverSelectedLine"

	boolean flat = false;
    boolean mouseInButton = false
    boolean mouseInArrowArea = false
    boolean popupMenuOperation = false

    Map<String, Icon> regIcons = new HashMap<String, Icon>(5)
    Map<String, Icon> arrowIcons = new HashMap<String, Icon>(5)

    PopupMenuListener menuListener

    int arrowSize = 6
    int gapSize = 6

    def menuGetter

    SplitButton(Icon icon) {

        setIcon(icon)
        resetIcons()

        mouseMoved = {e ->
            mouseInArrowArea = isInArrowArea(e.getPoint())
            updateRollover(getRolloverIconInternal(), getRolloverSelectedIconInternal())
        }

        mousePressed = {e ->

            popupMenuOperation = false
            JPopupMenu menu = getPopupMenu()
            if (menu != null && getModel() instanceof SplitButtonModel) {
                SplitButtonModel model = (SplitButtonModel)getModel()
                if (!model.isPressedInternal()) {
                    if (isInArrowArea(e.getPoint()) && menu.getComponentCount() > 0) {
                        model.pressInternal()
                        menu.addPopupMenuListener(getMenuListener())
                        menu.show(this, 0, getHeight())
                        popupMenuOperation = true
                    }
                }
                else {
                    model.releaseInternal()
                    menu.removePopupMenuListener(getMenuListener())
                    popupMenuOperation = true
                }
            }
        }

        mouseReleased = {e ->

            if (popupMenuOperation) {
                popupMenuOperation = false
                e.consume()
            }
        }

        mouseEntered = {e ->

			if(flat) {
				borderPainted = enabled
			}
			
            mouseInButton = true
            mouseInArrowArea = isInArrowArea(e.getPoint())
            updateRollover(getRolloverIconInternal(), getRolloverSelectedIconInternal())
        }

        mouseExited = {e ->

			if(flat && !((SplitButtonModel)getModel()).buttonPressed) {
				borderPainted = false
			}
			
            mouseInButton = false
            mouseInArrowArea = false
            updateRollover(getRolloverIconInternal(), getRolloverSelectedIconInternal())
        }

		flat = true
		borderPainted = false
        setModel(new SplitButtonModel(this))
    }

	public void setFlat(boolean f) {
		
		flat = f;
		borderPainted = f;
	}
	
	public boolean isFlat() {

		flat		
	}

    private PopupMenuListener getMenuListener() {

        if (null == menuListener) {
            menuListener = [
                popupMenuWillBecomeVisible: {e ->
                },

                popupMenuWillBecomeInvisible: {e ->

					if(flat) {
						borderPainted = false
					}
                    if (getModel() instanceof SplitButtonModel) {
                        ((SplitButtonModel)getModel()).releaseInternal()
                    }
                    JPopupMenu menu = getPopupMenu()
                    if (null != menu) {
                        menu.removePopupMenuListener(menuListener)
                    }
                },

                popupMenuCanceled: {e ->
                }
            ] as PopupMenuListener
        }
        return menuListener
    }

    private void updateRollover(Icon rollover, Icon rolloverSelected) {

        super.setRolloverIcon(rollover)
        super.setRolloverSelectedIcon(rolloverSelected)
    }

    private void resetIcons() {

        Icon icon = regIcons.get(ICON_NORMAL)
        if (null != icon)
            setIcon(icon)

        icon = regIcons.get(ICON_PRESSED)
        if (null != icon)
            setPressedIcon(icon)

        icon = regIcons.get(ICON_ROLLOVER)
        if (null != icon)
            setRolloverIcon(icon)

        icon = regIcons.get(ICON_ROLLOVER_SELECTED)
        if (null != icon)
            setRolloverSelectedIcon(icon)

        icon = regIcons.get(ICON_SELECTED)
        if (null != icon)
            setSelectedIcon(icon)

        icon = regIcons.get(ICON_DISABLED)
        if (null != icon)
            setDisabledIcon(icon)

        icon = regIcons.get(ICON_DISABLED_SELECTED)
        if (null != icon)
            setDisabledSelectedIcon(icon)
    }

    private Icon getRolloverIconInternal() {

        Icon icon = null
        icon = arrowIcons.get(mouseInArrowArea ? ICON_ROLLOVER : ICON_ROLLOVER_LINE)
        if (null == icon) {
            Icon orig = regIcons.get(ICON_ROLLOVER)
            if (null == orig)
                orig = regIcons.get(ICON_NORMAL)
            icon = new SplitButtonIcon(orig, !mouseInArrowArea, arrowSize, gapSize)
            arrowIcons.put(mouseInArrowArea ? ICON_ROLLOVER : ICON_ROLLOVER_LINE, icon)
        }
        return icon
    }

    private Icon getRolloverSelectedIconInternal() {

        Icon icon = null
        icon = arrowIcons.get(mouseInArrowArea ? ICON_ROLLOVER_SELECTED : ICON_ROLLOVER_SELECTED_LINE)
        if (null == icon) {
            Icon orig = regIcons.get(ICON_ROLLOVER_SELECTED)
            if (null == orig)
                orig = regIcons.get(ICON_ROLLOVER)
            if (null == orig)
                orig = regIcons.get(ICON_NORMAL)
            icon = new SplitButtonIcon(orig, !mouseInArrowArea, arrowSize, gapSize)
            arrowIcons.put(mouseInArrowArea ? ICON_ROLLOVER_SELECTED : ICON_ROLLOVER_SELECTED_LINE, icon)
        }
        return icon
    }

    private boolean isInArrowArea(Point p) {

        int arrowwidth = gapSize / 2 + arrowSize
        return p.getLocation().x >= getWidth() - arrowwidth - getInsets().right
    }

    @Override
    public void setIcon(Icon icon) {

        Icon arrow = updateIcons(icon, ICON_NORMAL)
        arrowIcons.remove(ICON_ROLLOVER_LINE)
        arrowIcons.remove(ICON_ROLLOVER_SELECTED_LINE)
        arrowIcons.remove(ICON_ROLLOVER)
        arrowIcons.remove(ICON_ROLLOVER_SELECTED)
        super.setIcon(arrow)
    }

    private Icon updateIcons(Icon orig, String iconType) {

        Icon arrow = null
        if (null == orig) {
            regIcons.remove(iconType)
            arrowIcons.remove(iconType)
        }
        else {
            regIcons.put(iconType, orig)
            arrow = new ImageIcon(icon2Image(new SplitButtonIcon(orig, false, arrowSize, gapSize)))
            arrowIcons.put(iconType, arrow)
        }
        return arrow
    }

    @Override
    public void setPressedIcon(Icon icon) {

        Icon arrow = updateIcons(icon, ICON_PRESSED)
        super.setPressedIcon(arrow)
    }

    @Override
    public void setSelectedIcon(Icon icon) {

        Icon arrow = updateIcons(icon, ICON_SELECTED)
        super.setSelectedIcon(arrow)
    }

    @Override
    public void setRolloverIcon(Icon icon) {

        Icon arrow = updateIcons(icon, ICON_ROLLOVER)
        arrowIcons.remove(ICON_ROLLOVER_LINE)
        arrowIcons.remove(ICON_ROLLOVER_SELECTED_LINE)
        super.setRolloverIcon(arrow)
    }

    @Override
    public void setRolloverSelectedIcon(Icon icon) {

        Icon arrow = updateIcons(icon, ICON_ROLLOVER_SELECTED)
        arrowIcons.remove(ICON_ROLLOVER_SELECTED_LINE)
        super.setRolloverSelectedIcon(arrow)
    }

    @Override
    public void setDisabledIcon(Icon icon) {

        Icon arrow = updateIcons(icon, ICON_DISABLED)
        super.setDisabledIcon(arrow)
    }

    @Override
    public void setDisabledSelectedIcon(Icon icon) {

        Icon arrow = updateIcons(icon, ICON_DISABLED_SELECTED)
        super.setDisabledSelectedIcon(arrow)
    }

    @Override
    public void setText(String text) {

    }

    @Override
    public String getText() {

        return null
    }

    private JPopupMenu getPopupMenu() {

        if(menuGetter != null) {
            return menuGetter()
        }
        else {
            return null
        }
    }

    private static final Image icon2Image(Icon icon) {

        if (icon instanceof ImageIcon) {
            return ((ImageIcon)icon).getImage()
        }
        else {
            BufferedImage image = new BufferedImage(icon.getIconWidth(), icon.getIconHeight(), BufferedImage.TYPE_INT_ARGB)
            Graphics g = image.getGraphics()
            icon.paintIcon(new JLabel(), g, 0, 0)
            g.dispose()
            return image
        }
    }
}
